package sim1011.poltergeist;

import java.util.ArrayList;
import java.util.HashMap;

import sim1011.components.Component;
import sim1011.starter.EctoMobileStarter;
import sim1011.status.Status;

public class CostArbiter extends Arbiter {
	private static final long serialVersionUID = 321226630412361166L;
	private float cost;
	private HashMap<String, Float> costRank = new HashMap<String, Float>();
	private HashMap<String, Float> finalRank;
	private float max = Float.MIN_VALUE;
	private float min = Float.MAX_VALUE;
	private Status status;

	public CostArbiter(Status s, float cost) {
		this.cost = cost;
		this.status = s;		
	}

	private float costExecutionOnNode(String comp, String cont) {
		float execution_cost = 0.0f;
		float reqCPU = status.getComponentsRequiredCPU().get(comp).longValue();
		float reqMEM = status.getComponentsRequiredMemory().get(comp)
				.floatValue();
		// il costo di esecuzione è la quantità di cpu e memoria richiesta
		// moltiplicata per il costo unitario di cpu e memoria del nodo che si
		// considera
		execution_cost = reqCPU
				* status.getResourceManagersCPUCost().get(cont).floatValue()
				* EctoMobileStarter.CPU_WEIGHT + reqMEM
				* status.getResourseManagersMemoryCost().get(cont).floatValue()
				* EctoMobileStarter.MEMORY_WEIGHT;
		return execution_cost;
	}

	private float costMigrationOnNode(String comp, String mitt, String dest) {
		float migration_cost = 0.0f;
		// calcola la packet size come un overhead di migrazione +
		// un ipotetico payload funziona di memoria e cpu richiesti dal
		// componente
		float packet_size = EctoMobileStarter.MIGRATION_OVERHEAD
				+ Component.size(status.getComponentsRequiredCPU().get(comp).intValue(),
						status.getComponentsRequiredMemory().get(comp).intValue());
		// calcola il migration cost come funzione della qualità del
		// collegamento tra nodo
		// mittente (mitt) e nodo destinatario (dest) e della packet size
		float quality = status.getLinkQoS(mitt, dest).floatValue();
		// più è grande il pacchetto più il costo di migrazione aumenta
		// più è piccola la qualità più è costoso (ci mette idealmente più
		// tempo) fare
		// la migrazione
		quality = quality - 5.0f;
		if (quality != 0.0f)
			migration_cost = packet_size * (1 / quality);
		else
			migration_cost = packet_size * (1/0.0001f);
		return migration_cost;
	}

	public void makeYourDecision(String comp, String cont,
			ArrayList<String> availableNodes, HashMap<String, Float> finalRank) {
		this.finalRank = finalRank;
		int size = availableNodes.size();
		for (int i = 0; i < size; ++i) {
			String curNode = availableNodes.get(i);
			// calcola costo come: costo esecuzione + costo migrazione
			float cur_cost;
			if (!curNode.equals(cont))
				cur_cost = (costExecutionOnNode(comp, curNode) + costMigrationOnNode(
						comp, cont, curNode));
			// se il nodo è quello attuale il costo è solo di esecuzione
			else
				cur_cost = costExecutionOnNode(comp, cont);
			// aggiorna minimo
			if (cur_cost < min)
				min = cur_cost;
			// aggiorna massimo
			if (cur_cost > max)
				max = cur_cost;
			costRank.put(curNode, new Float(cur_cost));
		}
		for (int i = 0; i < size; ++i) {
			String curNode = availableNodes.get(i);
			finalRank.put(
					curNode,
					finalRank.get(curNode)
							+ (EctoMobileStarter.MAX_RANK_VAL - (Arbiter.range(
									max, min, costRank.get(curNode)))) * EctoMobileStarter.COST_P);
		}
	}
}
